﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Pavilion;

namespace Pavilion
{
    public class AnimationPlayer
    {
        public static int FramesPerSecond = 12;

        public event Action<Animation> Complete;
        public event Action<Frame> FrameChanged;

        public bool IsReverse { get; set; }
        public Animation Animation { get { return animation; } set { SetAnimation(value); } }
        public Frame CurrentFrame { get { return animation.FrameList[(int)CurrentFrameIndex]; } }

        private Animation animation;
        private float currentFrameIndex;

        public virtual void Dispose()
        {
            animation = null;
            Complete = null;
            FrameChanged = null;
        }

        public void Animate(GameTime gameTime, float size)
        {
            if (animation == null)
                return;

            if (IsReverse)
                CurrentFrameIndex -= FramesPerSecond * (float)gameTime.ElapsedGameTime.TotalSeconds;
            else
                CurrentFrameIndex += FramesPerSecond * (float)gameTime.ElapsedGameTime.TotalSeconds;
        }

        private void AnimationComplete()
        {
            Reset();
            OnComplete();
        }

        private void Reset()
        {
            if (animation == null)
                return;

            if (IsReverse)
                currentFrameIndex = animation.FrameList.Count - 0.00001f;
            else
                currentFrameIndex = 0f;
        }

        /// <summary>
        /// Gets or sets the current frame Index;
        /// </summary>
        public float CurrentFrameIndex
        {
            get { return currentFrameIndex; }
            set
            {
                if (currentFrameIndex == value)
                    return;

                if ((int)currentFrameIndex == (int)value)
                {
                    currentFrameIndex = value;
                    return;
                }

                currentFrameIndex = value;

                if (IsAnimationComplete)
                    AnimationComplete();

                OnFrameChanged();
            }
        }
 
        private void SetAnimation(Pavilion.Animation value)
        {
            if (animation == value)
                return;

            animation = value;

            Reset();
        }

        private bool IsAnimationComplete
        {
            get
            {
                if (animation == null)
                    return false;
                if (IsReverse)
                    return (CurrentFrameIndex < 0);
                else
                    return (CurrentFrameIndex > animation.FrameList.Count);
            }
        }

        private void OnFrameChanged()
        {
            if (FrameChanged != null)
                FrameChanged(CurrentFrame);
        }

        private void OnComplete()
        {
            if (Complete != null)
                Complete(Animation);
        }

        public void Draw(Vector2 position, float size, Vector2 origin, SpriteEffects effects)
        {
            if (animation == null)
                return;

            animation.Draw((int)currentFrameIndex, position, size, origin, effects);
        }
    }

    public class NodeMonitorAnimationPlayer : AnimationPlayer
    {
        public event Action<AnimationNode> AnimationNodeLoaded, AnimationNodeUnloaded;

        public ContainerNodeMonitor NodeMonitor { get; private set; }

        public NodeMonitorAnimationPlayer(ContainerNodeMonitor nodeMonitor)
            : base()
        {
            this.NodeMonitor = nodeMonitor;

            nodeMonitor.BubbleUpNodeLoaded += new Action<ContainerNodeMonitor, NodeBase>(RootContainer_BubbleUpNodeLoaded);
            nodeMonitor.BubbleUpNodeUnloaded += new Action<ContainerNodeMonitor, NodeBase>(RootContainer_BubbleUpNodeUnloaded);

            UpdateCurrentAnimation();
        }

        public override void Dispose()
        {
            base.Dispose();

            AnimationNodeLoaded = null;
            AnimationNodeUnloaded = null;
            NodeMonitor.BubbleUpNodeLoaded -= new Action<ContainerNodeMonitor, NodeBase>(RootContainer_BubbleUpNodeLoaded);
            NodeMonitor.BubbleUpNodeUnloaded -= new Action<ContainerNodeMonitor, NodeBase>(RootContainer_BubbleUpNodeUnloaded);
            NodeMonitor = null;
        }

        public void UpdateCurrentAnimation()
        {
            AnimationNode animationNode = NodeMonitor.RootCurrentNode as AnimationNode;

            if (animationNode == null)
                Animation = null;
            else
                Animation = animationNode.Animation;
        }

        private void RootContainer_BubbleUpNodeUnloaded(ContainerNodeMonitor nodeMonitor, NodeBase node)
        {
            if (node is AnimationNode)
            {
                Animation = null;
                OnAnimationNodeUnloaded((AnimationNode)node);
            }
        }

        private void RootContainer_BubbleUpNodeLoaded(ContainerNodeMonitor nodeMonitor, NodeBase node)
        {
            if (node is AnimationNode)
            {
                Animation = (node as AnimationNode).Animation;
                OnAnimationNodeLoaded(node as AnimationNode);
            }
        }

        private void OnAnimationNodeUnloaded(AnimationNode node)
        {
            if (AnimationNodeUnloaded != null)
                AnimationNodeUnloaded(node);
        }

        private void OnAnimationNodeLoaded(AnimationNode node)
        {
            if (AnimationNodeLoaded != null)
                AnimationNodeLoaded(node);
        }
    }
}
